package com.spring.theory.mvc.basic.exception;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;

/**
 * 全局异常处理
 * ExceptionHandlerExceptionResolver
 * - 可以处理嵌套异常
 *
 * @author H
 * @version 1.0
 * @date 2024-01-22 14:13
 */
@Slf4j
public class TestExceptionHandler {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExceptionWebConfig.class);
        ExceptionHandlerExceptionResolver resolver = context.getBean(ExceptionHandlerExceptionResolver.class);
        test1(resolver);
        test2(resolver);

    }

    /**
     * 返回 ModelAndView
     *
     * @param resolver
     * @throws NoSuchMethodException
     */
    private static void test2(ExceptionHandlerExceptionResolver resolver) throws NoSuchMethodException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        HandlerMethod handler = new HandlerMethod(new Controller1(), Controller1.class.getMethod("test"));
        ArrayIndexOutOfBoundsException exception = new ArrayIndexOutOfBoundsException("出现异常");
        ModelAndView modelAndView = resolver.resolveException(request, response, handler, exception);
        //从视图中获取
        log.info("视图信息：{},{}", modelAndView.getViewName(), modelAndView.getModel());

    }

    /**
     * 返回 json
     *
     * @param resolver
     * @throws NoSuchMethodException
     */
    private static void test1(ExceptionHandlerExceptionResolver resolver) throws NoSuchMethodException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        HandlerMethod handler = new HandlerMethod(new Controller1(), Controller1.class.getMethod("test"));
        /**
         * 对于嵌套异常，会一层层解析
         */
        Exception exception = new Exception(new RuntimeException(new ArithmeticException("算术出现异常")));
        //ArithmeticException exception = new ArithmeticException("算术出现异常");
        /**
         * ExceptionHandlerExceptionResolver 会去解析目标类中，能处理该异常的方法再反射调用；
         * 如果没有能处理该异常的方法，就不解析，返回
         */
        resolver.resolveException(request, response, handler, exception);
        //从响应体中获取
        log.info(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));

    }

    static class Controller1 {
        public void test() {
        }

        @ExceptionHandler
        @ResponseBody
        public Map<String, Object> testHandleJson(ArithmeticException e) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("error", e.getMessage());
            return map;
        }

        @ExceptionHandler
        public ModelAndView testHandleModelView(ArrayIndexOutOfBoundsException e) {
            return new ModelAndView("handleView", "error", e.getMessage());
        }

    }

    @Configuration
    static class ExceptionWebConfig {

        /**
         * 配置类级别注解，全局性处理（处理控制器级别的异常；如果是非控制器抛出的异常，如 filter 抛出的异常，则需要更上层tomcat服务器去处理）
         * 当没有匹配的异常处理方法时，会返回@ControllerAdvice内配置的默认异常处理方法
         */
        @ControllerAdvice
        static class ExceptionControllerAdvice {
            @ExceptionHandler
            @ResponseBody
            public Map<String, Object> defaultHandleException(Exception e) {
                HashMap<String, Object> map = new HashMap<>();
                map.put("error", e.getMessage());
                return map;
            }
        }

        /**
         * @ExceptionHandler 解析器
         */
        @Bean
        public ExceptionHandlerExceptionResolver exceptionHandler() {
            //HandlerExceptionResolver的实现类
            ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
            //添加消息转换器，方便解析成客户端需要的格式
            resolver.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
            //添加一些默认的方法参数解析器及方法返回值解析器；spring会回调该方法
            //resolver.afterPropertiesSet();
            return resolver;
        }

    }

}
